package com.zjj.spring.read.xml;

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.RegexpMethodPointcutAdvisor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.zjj.spring.read.xml.aop.Apology;
import com.zjj.spring.read.xml.aop.Greeting;
import com.zjj.spring.read.xml.aop.GreetingImpl;
import com.zjj.spring.read.xml.aop.GreetingTwoImpl;
import com.zjj.spring.read.xml.aop.springaop.GreetingAfterAdvice;
import com.zjj.spring.read.xml.aop.springaop.GreetingAroundAdvice;
import com.zjj.spring.read.xml.aop.springaop.GreetingBeforeAdvice;
import com.zjj.spring.read.xml.aop.springaop.GreetingBeforeAfterAdvice;
import com.zjj.spring.read.xml.aop.springaop.GreetingThrowAdvice;
import com.zjj.spring.read.xml.aop.springaop.two.GreetingTwoAroundAdvice;
import com.zjj.spring.read.xml.bean.CommonBean;
import com.zjj.spring.read.xml.event.EmailDto;
import com.zjj.spring.read.xml.event.EmailEvent;
import com.zjj.spring.read.xml.event.EmailPublisher;
import com.zjj.spring.read.xml.factorybean.MyFactoryBeanMethod;
import com.zjj.spring.read.xml.factorybean.factory.Person;


public class SpringSeniorXMLMain {

	public static final String BLANK4 = "    ";
	
	@SuppressWarnings("resource")
	public static void main(String[] args) {

		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		System.out.println("【-】**************************************Spring start**************************************\n");
		
		CommonBean commonBean = applicationContext.getBean(CommonBean.class);
		System.out.println(BLANK4 + "commonBean = " + commonBean);
		
		//factory bean
//		factoryBeanTest(applicationContext);
		//event
//		publishEvent(applicationContext);
		
		//spring - aop - coding
//		aopCodingTest();
//		spring - aop - xml config
//		aopXMLTest(applicationContext);
		
//		spring - aop - xml config
//		aopIntroXMLTest(applicationContext);
		
//		spring - aop - coding
//		aopAdvisorTest(applicationContext);
//		spring - aop - xml
//		aopAdvisorCodingTest();
//		spring - aspect - coding - 未实现
//		aopAspectCodingTest(applicationContext);
//		spring - aspect - xml 
		aopAspectXMLTest(applicationContext);
		

		System.out.println("\n【-】**************************************Spring destroy**************************************");
		destroy(applicationContext);
	}

	/**
	 * factory-method 和 FactoryBean
	 * 简要说明参见{@link com.zjj.spring.read.xml.factorybean.MyFactoryBeanMethod}
	 * 和{@link com.zjj.spring.read.xml.factorybean.factory.MyCarFactoryBean}
	 * @param applicationContext
	 */
	public static void factoryBeanTest(ApplicationContext applicationContext) {
		System.out.println("【FB】----------------Enter factoryBean ---------------");
		
		MyFactoryBeanMethod myFactoryBeanMethod = (MyFactoryBeanMethod) applicationContext.getBean("myFactoryBeanMethodOne");
		System.out.println("【FB】myFactoryBeanMethod = " + myFactoryBeanMethod.getName());
		String myFactoryBeanMethodString = (String) applicationContext.getBean("myFactoryBeanMethodString");
		System.out.println("【FB】myFactoryBeanMethodString = " + myFactoryBeanMethodString);
		String myStaticFactoryBeanMethod = (String) applicationContext.getBean("myStaticFactoryBeanMethod");
		System.out.println("【FB】myStaticFactoryBeanMethod = " + myStaticFactoryBeanMethod);
		
		Person zjj = (Person) applicationContext.getBean("zjj");
		System.out.println("【FB】Person = " + zjj);
		System.out.println("【FB】Car = " + zjj.getCar().getName());
		System.out.println("【FB】----------------Exit  factoryBean---------------");
	}

	/**
	 * 简要说明参见{@link com.zjj.spring.read.xml.event.EmailPublisher}
	 * @param applicationContext
	 */
	public static void publishEvent(ClassPathXmlApplicationContext applicationContext) {
		System.out.println("【FB】----------------Enter publishEvent ---------------");
		EmailDto dto = new EmailDto();
		dto.setReceiveName("zjj");
		dto.setPort(1234);
		EmailEvent emailEvent = new EmailEvent(dto);
		EmailPublisher emailPublisher = applicationContext.getBean(EmailPublisher.class);
		//可以使用自定义发布器，也可以使用ApplicationContext发布事件(存在继承关系)
		emailPublisher.publishEvent(emailEvent);
		System.out.println("【FB】----------------publish event again ---------------");
		applicationContext.publishEvent(emailEvent);
		System.out.println("【FB】----------------Enter publishEvent ---------------");
	}
	/**
	 * 编程方式实现aop advice - 方法增强
	 */
	public static void aopCodingTest() {
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.setTarget(new GreetingImpl());
		//order 1
		proxyFactory.addAdvice(new GreetingBeforeAdvice());
		proxyFactory.addAdvice(new GreetingAfterAdvice());
		//order 2
		proxyFactory.addAdvice(new GreetingBeforeAfterAdvice());
		//order 3
		proxyFactory.addAdvice(new GreetingAroundAdvice());
		//except
		proxyFactory.addAdvice(new GreetingThrowAdvice());
		//get Proxy
		Greeting greeting = (Greeting) proxyFactory.getProxy();
		System.out.println(greeting.hello("zjj"));
		System.out.println(greeting.except("fffffff"));
	}
	
	/**
	 * XML配置实现aop advice - 方法增强
	 */
	public static void aopXMLTest(ClassPathXmlApplicationContext applicationContext) {
		Greeting greeting = (Greeting) applicationContext.getBean("greetingProxy");
		System.out.println(greeting.hello("apoXML"));
		System.out.println(greeting.except("fffffff"));
	
	}
	/**
	 * XML配置实现aop introduction - 类增强
	 */
	public static void aopIntroXMLTest(ClassPathXmlApplicationContext applicationContext) {
		Greeting greeting = (Greeting) applicationContext.getBean("greetingIntroProxy");
		/**这里也会被GreetingTwoAroundAdvice代理，因为在xml里面也配置了自动扫描代理类BeanNameAutoProxyCreator
		 * 它的规则就是所有的Impl结尾的类，刚好这里的GreetingAspectImpl符合这个规则
		 * */
		System.out.println(greeting.hello("apoXML"));
		/**这里的强制转型不会报错 - 这就是引入增强代理的特性 - (运行期)"动态接口实现"*/
		Apology apology = (Apology) greeting;
		apology.saySorry("zzzzzz");
	}
	/**
	 * 编程方式实现aop advisor- 切面
	 */
	public static void aopAdvisorCodingTest() {
		ProxyFactory proxyFactory = new ProxyFactory();
		Greeting greeting = new GreetingTwoImpl();
		
		RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor();
		advisor.setAdvice(new GreetingTwoAroundAdvice());
		//GreetingTwoImpl类中以good开头的方法 
		advisor.setPattern("com.zjj.spring.read.xml.aop.GreetingTwoImpl.good.*");
		
		proxyFactory.setTarget(greeting);
		proxyFactory.addAdvisor(advisor);
		
		//get Proxy
		Greeting proxy = (Greeting) proxyFactory.getProxy();
		System.out.println(proxy.hello("zjj"));				//不会被增强
		System.out.println(proxy.goodMorning("wwwwww"));	//可以被增强
	}
	/**
	 * XML配置实现aop advisor - 切面
	 */
	public static void aopAdvisorTest(ClassPathXmlApplicationContext applicationContext) {
		Greeting greeting = (Greeting) applicationContext.getBean("greetingTwoProxy");
		System.out.println(greeting.hello("zjj"));
		System.out.println(greeting.goodMorning("zzzzz"));
	}
	/**
	 * 编码实现spring aspect
	 */
	public static void aopAspectCodingTest(ClassPathXmlApplicationContext applicationContext) {
//		AspectJExpressionPointcut aep = new AspectJExpressionPointcut();
//		aep.setExpression("execution(* com.zjj.spring.read.xml.aop.GreetingAspectImpl.*(..)))");
//		Method method = 
//		AspectJAroundAdvice advice = new AspectJAroundAdvice(aspectJAroundAdviceMethod, aep, aif)
	}
	/**
	 * XML配置实现spring aspect
	 */
	public static void aopAspectXMLTest(ClassPathXmlApplicationContext applicationContext) {
		Greeting greeting = (Greeting) applicationContext.getBean("greetingAspectImpl");
		/**这里也会被GreetingTwoAroundAdvice代理，因为在xml里面也配置了自动扫描代理类BeanNameAutoProxyCreator
		 * 它的规则就是所有的Impl结尾的类，刚好这里的GreetingAspectImpl符合这个规则
		 * */
		/**可被GreetingAspect#around增强(增强所有方法),不可被beforeTest增强(仅增强good开头的方法)*/
		System.out.println(greeting.hello("zjj"));
		/**可被GreetingAspect#around增强,可被beforeTest增强*/
		System.out.println(greeting.goodMorning("aaaaaaa"));
	}
	
	

	public static void destroy(ClassPathXmlApplicationContext applicationContext) {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//如果多态情况下，类型使用ConfigurableApplicationContext,可使用下面方法注册销毁容器的钩子
//		applicationContext.registerShutdownHook();
		applicationContext.destroy();
	}
}
